<?php

namespace pmvc\annotation;

use pmvc\cache\MemoryCacheImpl;

use PHPUnit_Framework_TestCase;
use Exception;
use stdClass;

class NoParamsAnnotation
	extends AbstractAnnotation {

	protected function getAllowedParameterNames() {
		return Array();
	}
	
}

class ParamsAnnotation
	extends AbstractAnnotation {

	const A_CONSTANT = "hello";

	protected function getAllowedParameterNames() {
		return Array("name", "age", "arrayParams", "objectParam", "howdy");
	}

	public function testMethod() {
		return $this->name.":".$this->age;
	}
	
}

/**
 * This is a test class for annotations.
 * 
 * @\pmvc\annotation\NoParamsAnnotation()
 * @\pmvc\annotation\ParamsAnnotation(
 * 	howdy=self::A_CONSTANT,
 * 	name="my name",
 * 	age=12,
 * 	arrayParams=[@\pmvc\annotation\ParamsAnnotation(name="empty1"), @\pmvc\annotation\ParamsAnnotation(name="empty2")],
 *	objectParam=@\pmvc\annotation\NoParamsAnnotation()
 * )
 */
class AnnotatedClass {

	/**
	 * Whatever
	 * 
	 * @author bob villa
	 * @return mixed
	 * @\pmvc\annotation\ParamsAnnotation(name="property1")
	 */
	public $property1;

	/**
	 * Whatever
	 * 
	 * @author bob villa
	 * @return mixed
	 * @\pmvc\annotation\NoParamsAnnotation()
	 */
	public function method1() {
		
	}

	/**
	 * 
	 * @\pmvc\annotation\NoParamsAnnotation(invalid=true)
	 */
	public function methodWithInvalidAnnotation() {
		
	}
}

/**
 * This is just a function
 * 
 * @\pmvc\annotation\NoParamsAnnotation()
 * @author bob villa
 * @return mixed
 * @\pmvc\annotation\NoParamsAnnotation()
 */
function annotatedFunction() {
	
}

class AnnotationReaderImplTest
	extends PHPUnit_Framework_TestCase {

	public function testReadClassAnnotations() {
		
		// create it
		$reader = new AnnotationReaderImpl();
		$reader->setStrict();
		
		// read
		$annotations = $reader->readClassAnnotations('pmvc\annotation\AnnotatedClass');

		// verify
		$this->assertEquals(2, count($annotations), "Wrong number of annotations returned");
		$this->assertTrue($annotations[0] instanceof \pmvc\annotation\NoParamsAnnotation, "Wrong annotation type");
		$this->assertTrue($annotations[1] instanceof \pmvc\annotation\ParamsAnnotation, "Wrong annotation type");
		$this->assertEquals("my name", $annotations[1]->name);
		$this->assertEquals("my name", $annotations[1]["name"]);
		$this->assertEquals(12, $annotations[1]->age);
		$this->assertEquals(12, $annotations[1]["age"]);
		$this->assertEquals("my name:12", $annotations[1]->testMethod());
		$this->assertTrue(is_array($annotations[1]->arrayParams));
		$this->assertEquals(2, count($annotations[1]->arrayParams));
		$this->assertTrue($annotations[1]->arrayParams[0] instanceof \pmvc\annotation\ParamsAnnotation);
		$this->assertTrue($annotations[1]->arrayParams[1] instanceof \pmvc\annotation\ParamsAnnotation);
		$this->assertEquals("empty1", $annotations[1]->arrayParams[0]->name);
		$this->assertEquals("empty2", $annotations[1]->arrayParams[1]->name);
		$this->assertTrue($annotations[1]->objectParam instanceof \pmvc\annotation\NoParamsAnnotation);

	}

	public function testReadClassAnnotations_SpefificTypes() {
		
		// create it
		$reader = new AnnotationReaderImpl();
		$reader->setStrict();
		
		// read
		$annotations = $reader->readClassAnnotations('pmvc\annotation\AnnotatedClass', Array('pmvc\annotation\NoParamsAnnotation'));

		// verify
		$this->assertEquals(1, count($annotations), "Wrong number of annotations returned");
		$this->assertTrue($annotations[0] instanceof \pmvc\annotation\NoParamsAnnotation, "Wrong annotation type");
		
		// read
		$annotations = $reader->readClassAnnotations('pmvc\annotation\AnnotatedClass', Array('pmvc\annotation\ParamsAnnotation'));

		// verify
		$this->assertEquals(1, count($annotations), "Wrong number of annotations returned");
		$this->assertTrue($annotations[0] instanceof \pmvc\annotation\ParamsAnnotation, "Wrong annotation type");
		$this->assertEquals("my name", $annotations[0]->name);
		$this->assertEquals("my name", $annotations[0]["name"]);
		$this->assertEquals(12, $annotations[0]->age);
		$this->assertEquals(12, $annotations[0]["age"]);
		$this->assertEquals("my name:12", $annotations[0]->testMethod());
		$this->assertTrue(is_array($annotations[0]->arrayParams));
		$this->assertEquals(2, count($annotations[0]->arrayParams));
		$this->assertTrue($annotations[0]->arrayParams[0] instanceof \pmvc\annotation\ParamsAnnotation);
		$this->assertTrue($annotations[0]->arrayParams[1] instanceof \pmvc\annotation\ParamsAnnotation);
		$this->assertEquals("empty1", $annotations[0]->arrayParams[0]->name);
		$this->assertEquals("empty2", $annotations[0]->arrayParams[1]->name);
		$this->assertTrue($annotations[0]->objectParam instanceof \pmvc\annotation\NoParamsAnnotation);

	}

	public function testReadPropertyAnnotations() {
		
		// create it
		$reader = new AnnotationReaderImpl();
		$reader->setStrict();
		
		// read
		$annotations = $reader->readPropertyAnnotations(Array('pmvc\annotation\AnnotatedClass', "property1"));

		// verify
		$this->assertEquals(1, count($annotations), "Wrong number of annotations returned");
		$this->assertTrue($annotations[0] instanceof \pmvc\annotation\ParamsAnnotation, "Wrong annotation type");
		$this->assertEquals("property1", $annotations[0]->name);
	}

	public function testReadMethodAnnotations() {
		
		// create it
		$reader = new AnnotationReaderImpl();
		$reader->setStrict();
		
		// read
		$annotations = $reader->readMethodAnnotations(Array('pmvc\annotation\AnnotatedClass', "method1"));

		// verify
		$this->assertEquals(1, count($annotations), "Wrong number of annotations returned");
		$this->assertTrue($annotations[0] instanceof \pmvc\annotation\NoParamsAnnotation, "Wrong annotation type");
	}

	public function testReadFunctionAnnotations() {
		
		// create it
		$reader = new AnnotationReaderImpl();
		$reader->setStrict();
		
		// read
		$annotations = $reader->readFunctionAnnotations("\pmvc\annotation\annotatedFunction");

		// verify
		$this->assertEquals(2, count($annotations), "Wrong number of annotations returned");
		$this->assertTrue($annotations[0] instanceof \pmvc\annotation\NoParamsAnnotation, "Wrong annotation type");
		$this->assertTrue($annotations[0] instanceof \pmvc\annotation\NoParamsAnnotation, "Wrong annotation type");
	}

	public function testImmutability() {
		
		// create it
		$reader = new AnnotationReaderImpl();
		$reader->setStrict();
		
		// read
		$annotations = $reader->readPropertyAnnotations(Array('pmvc\annotation\AnnotatedClass', "property1"));
		$this->assertEquals(1, count($annotations), "Wrong number of annotations returned");

		$passed = false;
		try {
			$annotations[0]->property1 = "test";
		} catch(Exception $e) {
			$passed = true;
		}
		$this->assertTrue($passed, "Expected exception");

		$passed = false;
		try {
			$annotations[0]["property1"] = "test";
		} catch(Exception $e) {
			$passed = true;
		}
		$this->assertTrue($passed, "Expected exception");
	}

	public function testReadInvalidAnnotations() {
		
		// create it
		$reader = new AnnotationReaderImpl();
		$reader->setStrict();
		
		// read
		$passed = false;
		try {
			$annotations = $reader->readMethodAnnotations(Array('pmvc\annotation\AnnotatedClass', "methodWithInvalidAnnotation"));
		} catch(Exception $e) {
			$passed = true;
		}
		$this->assertTrue($passed, "Expected exception");
	}

	public function testCached() {
		
		// create it
		$realReader = new AnnotationReaderImpl();
		$realReader->setStrict();
		$reader = new CachedAnnotationReaderImpl($realReader);
		
		// read
		$annotations = $reader->readClassAnnotations('pmvc\annotation\AnnotatedClass');

		// verify
		$this->assertEquals(2, count($annotations), "Wrong number of annotations returned");
		$this->assertTrue($annotations[0] instanceof \pmvc\annotation\NoParamsAnnotation, "Wrong annotation type");
		$this->assertTrue($annotations[1] instanceof \pmvc\annotation\ParamsAnnotation, "Wrong annotation type");
		$this->assertEquals("my name", $annotations[1]->name);
		$this->assertEquals("my name", $annotations[1]["name"]);
		$this->assertEquals(12, $annotations[1]->age);
		$this->assertEquals(12, $annotations[1]["age"]);
		$this->assertEquals("my name:12", $annotations[1]->testMethod());
		$this->assertTrue(is_array($annotations[1]->arrayParams));
		$this->assertEquals(2, count($annotations[1]->arrayParams), "penis");
		$this->assertTrue($annotations[1]->arrayParams[0] instanceof \pmvc\annotation\ParamsAnnotation);
		$this->assertTrue($annotations[1]->arrayParams[1] instanceof \pmvc\annotation\ParamsAnnotation);
		$this->assertEquals("empty1", $annotations[1]->arrayParams[0]->name);
		$this->assertEquals("empty2", $annotations[1]->arrayParams[1]->name);
		$this->assertTrue($annotations[1]->objectParam instanceof \pmvc\annotation\NoParamsAnnotation);

	}

	public function testAnnotationUtil() {
		
		// create it
		$realReader = new AnnotationReaderImpl();
		$realReader->setStrict();
		$cache 	= new MemoryCacheImpl();
		$reader = new CachedAnnotationReaderImpl($realReader, $cache);
		AnnotationUtil::setAnnotationReader($reader);
		
		// read
		$annotats []= AnnotationUtil::getClassAnnotations('pmvc\annotation\AnnotatedClass');
		$annotats []= AnnotationUtil::getAnnotations(new AnnotatedClass());

		// verify
		foreach ($annotats as $annotations) {
			$this->assertEquals(2, count($annotations), "Wrong number of annotations returned");
			$this->assertTrue($annotations[0] instanceof \pmvc\annotation\NoParamsAnnotation, "Wrong annotation type 1");
			$this->assertTrue($annotations[1] instanceof \pmvc\annotation\ParamsAnnotation, "Wrong annotation type 2");
			$this->assertEquals("my name", $annotations[1]->name);
			$this->assertEquals("my name", $annotations[1]["name"]);
			$this->assertEquals(12, $annotations[1]->age);
			$this->assertEquals(12, $annotations[1]["age"]);
			$this->assertEquals("my name:12", $annotations[1]->testMethod());
			$this->assertTrue(is_array($annotations[1]->arrayParams));
			$this->assertEquals(2, count($annotations[1]->arrayParams));
			$this->assertTrue($annotations[1]->arrayParams[0] instanceof \pmvc\annotation\ParamsAnnotation);
			$this->assertTrue($annotations[1]->arrayParams[1] instanceof \pmvc\annotation\ParamsAnnotation);
			$this->assertEquals("empty1", $annotations[1]->arrayParams[0]->name);
			$this->assertEquals("empty2", $annotations[1]->arrayParams[1]->name);
			$this->assertTrue($annotations[1]->objectParam instanceof \pmvc\annotation\NoParamsAnnotation);
		}

	}

}

?>